package com.metadata_system.system.service;

import com.alibaba.fastjson.JSON;
import com.metadata_system.common.db.entity.*;
import com.metadata_system.common.db.mapper.*;
import com.metadata_system.common.dto.ApiResult;
import com.metadata_system.common.dto.SelectItem;
import com.metadata_system.common.enums.AllConst;
import com.metadata_system.common.exception.ApiException;
import com.metadata_system.common.service.ExcelReaderUtil;
import com.metadata_system.common.service.UserOperationLogService;
import com.metadata_system.common.utils.Base62Encoder;
import com.metadata_system.common.utils.DateTimeUtil;
import com.metadata_system.common.utils.MyUtil;
import com.metadata_system.system.request.ProjectListParams;
import com.metadata_system.system.request.ProjectLogListParams;
import com.metadata_system.system.response.NodeItem;
import com.metadata_system.system.response.NodePropertiesItem;
import com.metadata_system.system.response.ProjectDetailResponse;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import tk.mybatis.mapper.entity.Example;

import java.util.*;

@Service
public class ProjectService {

    @Value("${metadata-system.upload-path:/www/wwwroot/upload-file.metadata-system.cc/}")
    private String uploadPath;

    @Autowired(required = false)
    private ProjectMapper projectMapper;
    @Autowired(required = false)
    private ProjectUserMapper projectUserMapper;
    @Autowired(required = false)
    private ProjectNodeMapper projectNodeMapper;
    @Autowired(required = false)
    private MetadataMapper metadataMapper;
    @Autowired(required = false)
    private DatabaseMapper databaseMapper;
    @Autowired(required = false)
    ProjectLogMapper projectLogMapper;

    @Autowired(required = false)
    UserMapper userMapper;

    @Autowired
    UserOperationLogService userOperationLogService;
    @Autowired
    DatabaseService databaseService;


    /**
     * 获取项目列表
     *
     * @param listParams
     * @return
     */
    public ApiResult getList(ProjectListParams listParams) {
        Example example = new Example(Project.class);
        Example.Criteria criteria = example.createCriteria();
        if (!StringUtils.isBlank(listParams.getName())) {
            criteria.andLike("name", "%" + listParams.getName() + "%");
        }
        if (listParams.getMetadataNodeId() != null) {
            criteria.andEqualTo("metadataNodeId", listParams.getMetadataNodeId());
        }
        if (listParams.getDatabaseId() != null) {
            criteria.andEqualTo("databaseId", listParams.getDatabaseId());
        }
        if (listParams.getType() != null) {
            criteria.andEqualTo("type", listParams.getType());
        }
        listParams.setOrder(example, "id,updatedTime");
        if (!StringUtils.isBlank(listParams.getCreatedTime())) {
            listParams.setConditionTime(listParams.getCreatedTime(), criteria, "createdTime");
        }
        int total = this.projectMapper.selectCountByExample(example);
        List<Project> list = new ArrayList<>();
        if (total > 0) {
            list = this.projectMapper.selectByExampleAndRowBounds(example, listParams.getRowRunds());
        }
        return ApiResult.successPage(list, total);
    }


    public ApiResult getDetail(Integer id, Integer userId) {
        Project project = this.projectMapper.selectByPrimaryKey(id);
        if (project == null) {
            return ApiResult.error("项目不存在");
        }

        // 节点列表
        List<ProjectNode> projectNodeList = this.projectNodeMapper.select(ProjectNode.builder().projectId(id).build());
        ProjectDetailResponse projectDetailResponse = ProjectDetailResponse.fromEntity(project, projectNodeList);

        // 创建用户
        User user = this.userMapper.selectByPrimaryKey(project.getUserId());
        if (user == null) {
            return ApiResult.error("创建用户不存在");
        }
        Map<String, Object> map = new HashMap<String, Object>() {{
            put("id", user.getId());
            put("name", user.getName());
            put("username", user.getUsername());
            put("avatar", user.getAvatar());
        }};
        projectDetailResponse.setUser(map);
        projectDetailResponse.setUsername(user.getUsername());


        // 用户列表
        projectDetailResponse.setUserList(this.getProjectUserList(id).getData());


        // 所属数据库
        Database database = this.databaseMapper.selectByPrimaryKey(project.getDatabaseId());
        projectDetailResponse.setDatabaseName(database.getName());
        projectDetailResponse.setDatabaseId(database.getId());

        // 所属元数据
        Metadata metadata = this.metadataMapper.selectByPrimaryKey(project.getMetadataId());
        projectDetailResponse.setMetadataName(metadata.getName());
        projectDetailResponse.setMetadataId(metadata.getId());

        List<MetadataNode> metadataNodeList = this.metadataNodeMapper.selectDistinctByUniqueKeyByMetadataId(metadata.getId());
        projectDetailResponse.setMetadataNodeList(NodeItem.fromEntity(metadataNodeList));
        // 项目角色
        projectDetailResponse.setOwnRoles(this.projectUserMapper.getRoles(project.getId(), userId));

        return ApiResult.success(projectDetailResponse);
    }


    /**
     * 获取项目下的节点数据列表
     *
     * @param projectId 项目id
     * @param nodeId    节点id
     * @param searchMap 搜索条件
     * @param page      页码
     * @param pageSize  每页数量
     * @param sortProp  排序字段
     * @param sortOrder 排序顺序
     * @return
     */
    public ApiResult getNodeDataList(Integer projectId, Integer nodeId, Integer page, Integer pageSize, Map<String, Object> searchMap, String sortProp, String sortOrder) {
        ProjectNode projectNode = this.projectNodeMapper.selectOne(ProjectNode.builder()
                .projectId(projectId)
                .metadataNodeId(nodeId)
                .build());
        if (projectNode == null) {
            return ApiResult.error("项目节点 不存在");
        }
        if (page == null || page < 1) {
            page = 1;
        }
        if (pageSize == null || pageSize < 1 || pageSize > 500) {
            pageSize = 10;
        }

        List<String> constionList = MyUtil.getConditionList(searchMap, projectNode);


        Integer limit = pageSize;
        Integer offset = (page - 1) * pageSize;

        if (StringUtils.isBlank(sortProp) || !Arrays.asList("id").contains(sortProp)) {
            sortProp = "id";
        }
        if (!Arrays.asList("ASC", "DESC").contains(sortOrder.toUpperCase())) {
            sortOrder = "desc";
        }
        if (StringUtils.isBlank(sortOrder)) {
            sortOrder = "desc";
        }

        String order = " order by " + sortProp + " " + sortOrder + " ";

        int total = this.projectMapper.getNodeDataTotal(projectId, nodeId, constionList);
        if (total == 0) {
            return ApiResult.successPage(Collections.emptyList(), 0);
        }

        List<Map<String, Object>> nodeDataList = this.projectMapper.getNodeDataList(projectId, nodeId, offset, limit, constionList, order);
        for (Map<String, Object> stringObjectMap : nodeDataList) {
            stringObjectMap.put("createdTime", DateTimeUtil.formatDateTimetoString(new Date(projectNode.getCreatedTime().toString()), DateTimeUtil.FMT_yyyyMMddHHmmss));
        }
        return ApiResult.successPage(nodeDataList, total);
    }

    public ApiResult getProjectUserList(Integer projectId) {
        Example example1 = new Example(ProjectUser.class);
        Example.Criteria criteria1 = example1.createCriteria();
        criteria1.andEqualTo("projectId", projectId);
        int total = this.projectUserMapper.selectCountByExample(example1);
        if (total == 0) {
            return ApiResult.error("项目不存在");
        }


        List<ProjectUser> list = new ArrayList<>();
        HashMap<Integer, String> userRoleMap = new HashMap<>();
        if (total > 0) {
            list = this.projectUserMapper.selectByExample(example1);
        }

        List<Integer> userIds = new ArrayList<>();
        for (ProjectUser projectUser : list) {
            if (!userIds.contains(projectUser.getUserId())) userIds.add(projectUser.getUserId());
            userRoleMap.put(projectUser.getUserId(), projectUser.getRole());
        }

        Example example2 = new Example(User.class);
        example2.createCriteria().andIn("id", userIds);
        List<User> userList = this.userMapper.selectByExample(example2);

        List<HashMap<String, Object>> userMapList = new ArrayList<>();
        for (User user : userList) {
            HashMap<String, Object> map = new HashMap<>();
            map.put("id", user.getId());
            map.put("name", user.getName());
            map.put("username", user.getUsername());
            map.put("avatar", user.getAvatar());
            map.put("role", AllConst.IdName(userRoleMap.get(user.getId()), AllConst.ProjectRoleMap));
            userMapList.add(map);
        }

        return ApiResult.success(userMapList);
    }


    /**
     * 创建项目
     *
     * @param project
     * @param users
     * @return
     */
    public ApiResult create(Project project, String users, Integer userId) {
        Metadata metadataNode = metadataMapper.selectByPrimaryKey(project.getMetadataId());
        if (metadataNode == null) {
            return ApiResult.error("元数据 不存在");
        }
        if (project.getDatabaseId() != null) {
            Database database = databaseMapper.selectByPrimaryKey(project.getDatabaseId());
            if (database == null) {
                return ApiResult.error("数据库 不存在");
            }
        }
        if (StringUtils.isBlank(project.getShortName())) {
            return ApiResult.error("项目简称 不能为空");
        } else {
            if (project.getShortName().isEmpty() || project.getShortName().length() > 3) {
                return ApiResult.error("项目简称长度必须在1-3个字符之间");
            }
        }

        if (StringUtils.isBlank(project.getName())) {
            return ApiResult.error("项目名称 不能为空");
        } else {
            if (project.getName().isEmpty() || project.getName().length() > 20) {
                return ApiResult.error("项目名称 长度必须在1-20个字符之间");
            }
        }

        Project insertProject = Project.builder().build();
        insertProject.setType(project.getType());
        insertProject.setDatabaseId(project.getDatabaseId());
        insertProject.setDatabaseName(this.databaseMapper.selectByPrimaryKey(project.getDatabaseId()).getName());
        insertProject.setMetadataId(project.getMetadataId());
        insertProject.setMetadataName(metadataNode.getName());
        insertProject.setUserId(project.getUserId());
        insertProject.setName(project.getName());
        insertProject.setShortName(project.getShortName());
        insertProject.setDescription(project.getDescription());
        insertProject.setDataTotal(0);
        insertProject.setUpdatedTime(new Date());
        insertProject.setCreatedTime(new Date());

        this.projectMapper.insert(insertProject);
        insertProject.setId(this.projectMapper.getLastInsertId());

        // 插入项目用户
        try {
            List<ProjectUser> userList = JSON.parseArray(users, ProjectUser.class);
            Integer ownUserTotal = 0;
            for (ProjectUser user : userList) {
                if (user.getRole().equals("Owner")) {
                    ownUserTotal++;
                    if (ownUserTotal > 1) {
                        return ApiResult.error("项目只能有一个 项目拥有者");
                    }
                }
            }
            if (ownUserTotal == 0) {
                return ApiResult.error("项目必须有一个 项目拥有者");
            }
            for (ProjectUser projectUser : userList) {
                projectUser.setProjectId(insertProject.getId());
                this.projectUserMapper.insertSelective(projectUser);
            }
        } catch (Exception e) {
            return ApiResult.error("用户参数错误: " + e.getMessage());
        }

        databaseService.updateDatabaseProjectTotal(project.getDatabaseId(), userId);

        // 记录操作日志
        userOperationLogService.insert(UserOperationLog.builder()
                .userId(userId)
                .username(this.userMapper.selectByPrimaryKey(userId).getUsername())
                .type("Project.Create")
                .content("创建项目" + insertProject.getName())
                .params(insertProject.getId().toString())
                .createdTime(new Date())
                .build());

        return ApiResult.success();
    }


    /**
     * 更新项目
     *
     * @param projectParams
     * @param users
     * @return
     */
    public ApiResult update(Project projectParams, String users, Integer userId) {
        Project currProject = this.projectMapper.selectByPrimaryKey(projectParams.getId());
        if (currProject == null) {
            return ApiResult.error("项目不存在");
        }

        Project updateProject = Project.builder().id(projectParams.getId()).build();
        if (projectParams.getName() != null) {
            if (StringUtils.isBlank(projectParams.getName())) {
                return ApiResult.error("项目名称 不能为空");
            } else {
                if (projectParams.getName().isEmpty() || projectParams.getName().length() > 20) {
                    return ApiResult.error("项目名称 长度必须在1-20个字符之间");
                }
            }
            updateProject.setName(projectParams.getName());
        }


        if (projectParams.getShortName() != null) {
            if (StringUtils.isBlank(projectParams.getShortName())) {
                return ApiResult.error("项目简称 不能为空");
            } else {
                if (projectParams.getShortName().isEmpty() || projectParams.getShortName().length() > 3) {
                    return ApiResult.error("项目简称长度必须在1-3个字符之间");
                }
            }
            updateProject.setShortName(projectParams.getShortName());
        }


        if (currProject.getDescription() != null) {
            updateProject.setDescription(projectParams.getDescription());
        }

        // 元数据节点
        if (currProject.getMetadataId() != null) {
            if (currProject.getDataTotal() > 0) {
                return ApiResult.error("项目下有数据，不能修改元数据节点");
            }
            updateProject.setMetadataId(projectParams.getMetadataId());
            Metadata metadata = metadataMapper.selectByPrimaryKey(projectParams.getMetadataId());
            if (metadata == null) {
                return ApiResult.error("元数据 不存在");
            }
        }

        // 删除原有用户
        this.projectUserMapper.delete(ProjectUser.builder().projectId(currProject.getId()).build());

        // 重新插入新用户
        try {
            List<ProjectUser> userList = JSON.parseArray(users, ProjectUser.class);
            Integer ownUserTotal = 0;
            for (ProjectUser user : userList) {
                if (user.getRole().equals("Owner")) {
                    ownUserTotal++;
                    if (ownUserTotal > 1) {
                        return ApiResult.error("项目只能有一个 项目拥有者");
                    }
                }
            }
            if (ownUserTotal == 0) {
                return ApiResult.error("项目必须有一个 项目拥有者");
            }
            for (ProjectUser projectUser : userList) {
                projectUser.setProjectId(projectParams.getId());
                this.projectUserMapper.insertSelective(projectUser);
            }
        } catch (Exception e) {
            return ApiResult.error("用户参数错误");
        }

        updateProject.setUpdatedTime(new Date());
        this.projectMapper.updateByPrimaryKeySelective(updateProject);

        // 记录操作日志
        userOperationLogService.insert(UserOperationLog.builder()
                .userId(userId)
                .username(this.userMapper.selectByPrimaryKey(userId).getUsername())
                .type("Project.Update")
                .content("更新项目" + updateProject.getName())
                .params(updateProject.getId().toString())
                .createdTime(new Date())
                .build());
        return ApiResult.success();
    }


    /**
     * 删除项目
     *
     * @param id
     * @return
     */
    public ApiResult delete(Integer id, Integer userId) {
        Project project = this.projectMapper.selectByPrimaryKey(id);
        if (project == null) {
            return ApiResult.error("项目不存在");
        }
        if (project.getDataTotal() > 0) {
            return ApiResult.error("项目下有数据，不能删除");
        }
        Example example = new Example(Project.class);
        example.createCriteria().andEqualTo("id", id);
        this.projectMapper.deleteByExample(example);
        Example exampleUser = new Example(ProjectUser.class);
        exampleUser.createCriteria().andEqualTo("projectId", id);
        List<ProjectUser> userList = this.projectUserMapper.selectByExample(exampleUser);
        for (ProjectUser projectUser : userList) {
            this.messageService.addMessage(
                    "您的所在项目【" + project.getName() + "】已被删除",
                    "ProjectUser",
                    projectUser.getUserId(),
                    project.getId(),
                    project.getName(),
                    projectUser.getRole());
        }
        databaseService.updateDatabaseProjectTotal(project.getDatabaseId(), userId);

        // 记录操作日志
        userOperationLogService.insert(UserOperationLog.builder()
                .userId(userId)
                .username(this.userMapper.selectByPrimaryKey(userId).getUsername())
                .type("Project.Delete")
                .content("删除项目" + project.getName())
                .params(project.getId().toString())
                .createdTime(new Date())
                .build());
        return ApiResult.success();
    }

    @Autowired
    private MessageService messageService;


    @Autowired(required = false)
    private MetadataNodeMapper metadataNodeMapper;

    @SneakyThrows
    public ApiResult importProjectNode(String fileUrl, Integer projectId) {
        Project project = this.projectMapper.selectByPrimaryKey(projectId);
        if (project == null) {
            return ApiResult.error("项目不存在");
        }
        List<ExcelReaderUtil.ExcelSheetData> excelSheetDataList = ExcelReaderUtil.readExcel(uploadPath + fileUrl);


        List<String> nodeKeys = new ArrayList<>();

        for (ExcelReaderUtil.ExcelSheetData sheetData : excelSheetDataList) {
            nodeKeys.add(ProjectNode.getPropertiesKeyByNames(sheetData.getHeaders(), sheetData.getName()));
        }

        Example example = new Example(MetadataNode.class);
        example.createCriteria().andIn("propertiesKey", nodeKeys).andEqualTo("metadataId", project.getMetadataId());
        List<MetadataNode> metadataNodes = metadataNodeMapper.selectByExample(example);
        if (metadataNodes.isEmpty()) {
            return ApiResult.error("没有匹配到元数据节点，请检查: 页名称，表头头部信息");
        }
        Map<String, MetadataNode> metadataNodeMap = new HashMap<>();
        for (MetadataNode metadataNode : metadataNodes) {
            metadataNodeMap.put(metadataNode.getName(), metadataNode);
        }

        for (ExcelReaderUtil.ExcelSheetData sheetData : excelSheetDataList) {
            MetadataNode metadataNode = metadataNodeMap.get(sheetData.getName());
            if (metadataNode != null) {
                sheetData.setTableFieldList(JSON.parseArray(metadataNode.getProperties(), NodePropertiesItem.class));
                sheetData.setTableName("md_project_node_data_" + projectId + "_" + metadataNode.getId());
                HashMap<String, Object> nameFieldMap = new HashMap<>();
                for (NodePropertiesItem nodePropertiesItem : sheetData.getTableFieldList()) {
                    nameFieldMap.put(nodePropertiesItem.getLabel(), nodePropertiesItem.getProp());
                }

                // 设置表实际字段
                List<Map<String, Object>> tableDataList = new ArrayList<>();
                sheetData.getTableData().forEach((map) -> {
                    HashMap<String, Object> newMap = new HashMap<String, Object>() {
                    };
                    sheetData.getTableFieldList().forEach(field -> {
                        newMap.put(field.getProp(), map.get(field.getLabel()));
                    });
                    tableDataList.add(newMap);
                });
                sheetData.setTableData(tableDataList);
            } else {
                System.out.println("sheetData.getName:" + sheetData.getName() + " 不存在");
            }
        }


        ExcelReaderUtil.ExcelSheetData.checkErrorColumn(excelSheetDataList);

        return ApiResult.success(excelSheetDataList);
    }


    public ApiResult history(Integer projectId, Integer userId) {
        Project project = this.projectMapper.selectByPrimaryKey(projectId);
        if (project == null) {
            return ApiResult.error("项目不存在");
        }
        ProjectUser projectUser = this.projectUserMapper.selectOne(ProjectUser.builder()
                .projectId(projectId)
                .userId(userId)
                .build());
        if (projectUser == null) {
            return ApiResult.error("您不是项目成员");
        }
        return projectLogService.getList(ProjectLogListParams.builder()
                .showType("apply")
                .projectId(projectId)
                .userId(userId)
                .status(1)
                .build());
    }

    @Autowired
    ProjectLogService projectLogService;


    /**
     * 导出数据
     *
     * @param projectId       项目ID
     * @param userId          导出用户id
     * @param nodeNamesString 项目节点名称数组
     * @return
     */
    public ApiResult exportData(Integer projectId, Integer userId, String nodeNamesString) {
        // 1. 校验项目是否存在
        Project project = this.projectMapper.selectByPrimaryKey(projectId);
        if (project == null) {
            return ApiResult.error("项目不存在");
        }
        ProjectUser projectUser = this.projectUserMapper.selectOne(ProjectUser.builder()
                .projectId(projectId)
                .userId(userId)
                .build());
        if (projectUser == null) {
            return ApiResult.error("您不是项目成员");
        }

        // 2.查询导出节点
        List<String> nodeNameList = new ArrayList<>();
        if (StringUtils.isNotBlank(nodeNamesString)) {
            nodeNameList = JSON.parseArray(nodeNamesString, String.class);
        }
        Example example = new Example(ProjectNode.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("projectId", projectId);
        if (!nodeNameList.isEmpty()) {
            criteria.andIn("nodeName", nodeNameList);
        }
        List<ProjectNode> projectNodeList = this.projectNodeMapper.selectByExample(example);
        if (projectNodeList.isEmpty()) {
            return ApiResult.error("没有匹配到数据");
        }
        Integer total = 0;
        for (ProjectNode projectNode : projectNodeList) {
            total += projectNode.getDataTotal();
        }
        if (total == 0) {
            return ApiResult.error("当前节点没有数据");
        }
        if (total == 10000) {
            return ApiResult.error("导出数据量不能超过10000条");
        }

        // 3.构造导出数据
        List<ExcelReaderUtil.ExcelSheetData> excelSheetDataList = new ArrayList<>();
        for (ProjectNode projectNode : projectNodeList) {
            if (projectNode.getDataTotal() > 0) {
                List<Map<String, Object>> nodeDataList = this.projectMapper.getNodeDataList(projectNode.getProjectId(), projectNode.getMetadataNodeId(), 0, 10000, new ArrayList<>(), " order by id desc");
                if (!nodeDataList.isEmpty()) {
                    excelSheetDataList.add(ExcelReaderUtil.ExcelSheetData.builder()
                            .name(projectNode.getMetadataNodeName())
                            .tableFieldList(JSON.parseArray(projectNode.getMetadataNodeProperties(), NodePropertiesItem.class))
                            .tableData(nodeDataList)
                            .build());
                }
            }
        }


        // 4.生成导出文件
        String fileNamePath = "/export-file/" + Base62Encoder.encode(System.currentTimeMillis()) + ".xls";
        ExcelReaderUtil.exportExcel(excelSheetDataList, uploadPath + fileNamePath);

        return ApiResult.success(fileNamePath);
    }

    /**
     * 更新项目数据量
     *
     * @param projectId
     * @param userId
     */
    public void updateDataTotal(Integer projectId, Integer userId) {
        Project project = this.projectMapper.selectByPrimaryKey(projectId);
        if (project == null) {
            throw new ApiException("项目不存在");
        }

        this.projectNodeMapper.getProjectDataTotal(projectId);
        Integer dataTotal = this.projectNodeMapper.getProjectDataTotal(projectId);
        this.projectMapper.updateByPrimaryKeySelective(Project.builder()
                .id(projectId)
                .dataTotal(dataTotal)
                .build());

        this.userOperationLogService.insert(UserOperationLog.builder()
                .userId(userId)
                .username(userMapper.selectByPrimaryKey(userId).getUsername())
                .type("Project.Update")
                .content("更新项目【" + project.getName() + "】数据量为：" + dataTotal)
                .params(projectId.toString())
                .createdTime(new Date())
                .build());
    }

}
